package org.xxpay.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.exception.ServiceException;
import org.xxpay.core.common.util.DateUtil;
import org.xxpay.core.entity.MchAccount;
import org.xxpay.core.entity.MchAgentpayRecord;
import org.xxpay.core.entity.MchAgentpayRecordExample;
import org.xxpay.core.entity.TransOrder;
import org.xxpay.core.service.IMchAccountService;
import org.xxpay.core.service.IMchAgentpayService;
import org.xxpay.core.service.ITransOrderService;
import org.xxpay.service.common.annotation.DataSourceSwitch;
import org.xxpay.service.common.enumm.DataSourceType;
import org.xxpay.service.dao.mapper.MchAgentpayRecordMapper;
import org.xxpay.service.dao.mapper.TransOrderMapper;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: dingzhiwei
 * @date: 2018/4/21
 * @description:
 */
@Service
public class MchAgentpayServiceImpl implements IMchAgentpayService {

    @Autowired
    private IMchAccountService mchAccountService;

    @Autowired
    private ITransOrderService transOrderService;

    @Autowired
    private MchAgentpayRecordMapper mchAgentpayRecordMapper;

    @Autowired
    private TransOrderMapper transOrderMapper;

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void frozenAmountAndInsertRecord(Long agentpayAmount, List<MchAgentpayRecord> recordList) {

        if(recordList == null || recordList.isEmpty()) return ;
        byte subAmountFrom = recordList.get(0).getSubAmountFrom();
        Long mchId = recordList.get(0).getMchId();
        byte mchType = recordList.get(0).getMchType();

        if(mchType == MchConstant.MCH_TYPE_PRIVATE){ //私有商户， 无需判断账户余额
            for(MchAgentpayRecord record : recordList) {
                mchAgentpayRecordMapper.insertSelective(record);

                TransOrder transOrder = record.getPsVal("transOrder", TransOrder.class);
                if(transOrder != null){
                    transOrderService.createTransOrder(transOrder);
                }
            }
            return ;
        }

        // 查询商户账户信息,判断金额是否够结算
        MchAccount mchAccount = mchAccountService.findByMchId(mchId);
        if(mchAccount == null) {
            // 账户不存在
            throw ServiceException.build(RetEnum.RET_SERVICE_ACCOUNT_NOT_EXIST);
        }
        // 确定出款账户,判断是否足够出款
        if(MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE == subAmountFrom) { // 从收款账户余额出款
            // 可结算金额
            Long availableAmount = mchAccount.getAvailableSettAmount();
            if(agentpayAmount.compareTo(availableAmount) > 0) {
                // 金额超限
                throw ServiceException.build(RetEnum.RET_SERVICE_SETT_AMOUNT_NOT_SETT);
            }
            // 冻结资金操作
            mchAccountService.onlyChangeUnBalance(mchId, MchConstant.BIZ_ITEM_BALANCE, agentpayAmount);
        }else if(MchConstant.AGENTPAY_OUT_ACCOUNT_AGBALANCE == subAmountFrom) { // 从代付账户余额出款
            // 可用代付余额
            Long AvailableAgentpayBalance = mchAccount.getAvailableAgentpayBalance();
            if(agentpayAmount.compareTo(AvailableAgentpayBalance) > 0) {
                // 金额超限
                throw ServiceException.build(RetEnum.RET_SERVICE_SETT_AMOUNT_NOT_SETT);
            }
            // 冻结资金操作
            mchAccountService.onlyChangeUnBalance(mchId, MchConstant.BIZ_ITEM_AGENTPAY_BALANCE, agentpayAmount);
        }else {
            throw ServiceException.build(RetEnum.RET_SERVICE_AGENTPAY_OUT_ERROR);
        }

        for(MchAgentpayRecord record : recordList) {
            mchAgentpayRecordMapper.insertSelective(record);
            TransOrder transOrder = record.getPsVal("transOrder", TransOrder.class);
            if(transOrder != null){
                transOrderService.createTransOrder(transOrder);
            }
        }

    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void updateStatus4Success(String agentpayOrderId, String transOrderId, String channelOrderNo, boolean isTccard) {

        MchAgentpayRecord updateMchAgentpayRecord = new MchAgentpayRecord();
        updateMchAgentpayRecord.setStatus(PayConstant.AGENTPAY_STATUS_SUCCESS);
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdEqualTo(agentpayOrderId);
        criteria.andStatusEqualTo(isTccard? PayConstant.AGENTPAY_STATUS_WAIT_AUDIT : PayConstant.AGENTPAY_STATUS_ING);


        //1. 更新mchAgentpayRecord表
        if(mchAgentpayRecordMapper.updateByExampleSelective(updateMchAgentpayRecord, example) < 1) {
            throw new ServiceException(RetEnum.RET_MCH_UPDATE_TRADE_ORDER_FAIL); //事物回滚
        }

        if(!isTccard){

            //2. 更新tradeOrder表
            transOrderService.updateStatus4Success(transOrderId, channelOrderNo);
        }

        //3. 查询最新记录 发起[解冻+减款]
        mchAccountService.changeBalanceAndInsertHistory(this.findByAgentpayOrderId(agentpayOrderId), transOrderId);
    }

    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void updateStatus4Fail(String agentpayOrderId, String transOrderId, String channelErrCode, String channelErrMsg, Byte recordOldStatus){
        MchAgentpayRecord updateMchAgentpayRecord = new MchAgentpayRecord();
        updateMchAgentpayRecord.setTransMsg(channelErrMsg);
        updateMchAgentpayRecord.setStatus(PayConstant.AGENTPAY_STATUS_FAIL);
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdEqualTo(agentpayOrderId);
        criteria.andStatusEqualTo(recordOldStatus);

        //1. 更新mchAgentpayRecord
        if(mchAgentpayRecordMapper.updateByExampleSelective(updateMchAgentpayRecord, example) < 1){
            throw new ServiceException(RetEnum.RET_MCH_UPDATE_TRADE_ORDER_FAIL); //事务回滚
        }

        //recordOldStatus 可能有三种状态： 1.处理中（自动处理的形式）  2.待审核（人工审核） 3.待接口确认

        if(recordOldStatus == PayConstant.AGENTPAY_STATUS_ING){
            //2. 更新transOrder
            transOrderService.updateStatus4Fail(transOrderId, channelErrCode, channelErrMsg);  //延续之前的处理逻辑， 其他状态无此订单表
        }

        // 查询代付记录  & 解冻金额
        MchAgentpayRecord mchAgentpayRecord = findByAgentpayOrderId(agentpayOrderId);

        if(mchAgentpayRecord.getMchType() == MchConstant.MCH_TYPE_PRIVATE) return ; //私有商户不做处理

        String bizItem = mchAgentpayRecord.getSubAmountFrom() == MchConstant.AGENTPAY_OUT_ACCOUNT_BALANCE ?
                         MchConstant.BIZ_ITEM_BALANCE : MchConstant.BIZ_ITEM_AGENTPAY_BALANCE;
        mchAccountService.onlyChangeUnBalance(mchAgentpayRecord.getMchId(), bizItem, (0 - mchAgentpayRecord.getAmount() - mchAgentpayRecord.getFee()));

    }


    @Override
    @Transactional(transactionManager = "transactionManager", rollbackFor = Exception.class)
    public void updateAudit2Ing(String agentpayOrderId, MchAgentpayRecord record){

        MchAgentpayRecord updateMchAgentpayRecord = new MchAgentpayRecord();
        updateMchAgentpayRecord.setStatus(PayConstant.AGENTPAY_STATUS_ING);

        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdEqualTo(agentpayOrderId);
        criteria.andStatusEqualTo(PayConstant.AGENTPAY_STATUS_WAIT_AUDIT);

        //1. 更新mchAgentpayRecord表
        if(mchAgentpayRecordMapper.updateByExampleSelective(record, example) < 1) {
            throw new ServiceException(RetEnum.RET_MCH_UPDATE_TRADE_ORDER_FAIL); //事物回滚
        }
    }

    @Override
    public int updateById(MchAgentpayRecord updateRecord){
        return mchAgentpayRecordMapper.updateByPrimaryKeySelective(updateRecord);
    }

    @Override
    public Integer count(MchAgentpayRecord mchAgentpayRecord) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord);
        return mchAgentpayRecordMapper.countByExample(example);
    }

    @Override
    public Integer snatchOrder(String agentpayOrderId, String snatchUserId, String snatchName) {
        MchAgentpayRecord updateMchAgentpayRecord = new MchAgentpayRecord();
        updateMchAgentpayRecord.setSnatchName(snatchName);
        updateMchAgentpayRecord.setSnatchUserId(snatchUserId);
        updateMchAgentpayRecord.setSnatchTime(new Date());
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdEqualTo(agentpayOrderId);
        criteria.andSnatchNameEqualTo("");
        criteria.andSnatchUserIdEqualTo("");
        criteria.andStatusEqualTo(PayConstant.AGENTPAY_STATUS_WAIT_AUDIT);
        return mchAgentpayRecordMapper.updateByExampleSelective(updateMchAgentpayRecord, example);
    }

    @Override
    public Integer giveUpOrder(String agentpayOrderId, String snatchUserId, boolean isSupAdmin) {
        MchAgentpayRecord updateMchAgentpayRecord = new MchAgentpayRecord();
        updateMchAgentpayRecord.setSnatchUserId("");
        updateMchAgentpayRecord.setSnatchName("");
        updateMchAgentpayRecord.setGiveUpTime(new Date());
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdEqualTo(agentpayOrderId);
        if(!isSupAdmin) criteria.andSnatchUserIdEqualTo(snatchUserId);
        criteria.andStatusEqualTo(PayConstant.AGENTPAY_STATUS_WAIT_AUDIT);
        return mchAgentpayRecordMapper.updateByExampleSelective(updateMchAgentpayRecord, example);
    }

    @Override
    public List<MchAgentpayRecord> select(int offset, int limit, MchAgentpayRecord mchAgentpayRecord, JSONObject queryObj) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        example.setOrderByClause("createTime desc");
        example.setOffset(offset);
        example.setLimit(limit);
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord, queryObj);
        return mchAgentpayRecordMapper.selectByExample(example);
    }

    @Override
    public int count(MchAgentpayRecord mchAgentpayRecord, JSONObject queryObj) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord, queryObj);
        return mchAgentpayRecordMapper.countByExample(example);
    }

    @Override
    public MchAgentpayRecord find(MchAgentpayRecord mchAgentpayRecord) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord);
        List<MchAgentpayRecord> mchAgentpayRecordList = mchAgentpayRecordMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(mchAgentpayRecordList)) return null;
        return mchAgentpayRecordList.get(0);
    }

    @Override
    public MchAgentpayRecord findByTransOrderId(String transOrderId) {
        MchAgentpayRecord mchAgentpayRecord = new MchAgentpayRecord();
        mchAgentpayRecord.setTransOrderId(transOrderId);
        return find(mchAgentpayRecord);
    }

    @Override
    public MchAgentpayRecord findByAgentpayOrderId(String agentpayOrderId) {
        return mchAgentpayRecordMapper.selectByPrimaryKey(agentpayOrderId);
    }

    @Override
    public MchAgentpayRecord findByMchIdAndAgentpayOrderId(Long mchId, String agentpayOrderId) {
        MchAgentpayRecord mchAgentpayRecord = new MchAgentpayRecord();
        mchAgentpayRecord.setMchId(mchId);
        mchAgentpayRecord.setAgentpayOrderId(agentpayOrderId);
        return find(mchAgentpayRecord);
    }

    @Override
    public MchAgentpayRecord findByMchIdAndMchOrderNo(Long mchId, String mchOrderNo) {
        MchAgentpayRecord mchAgentpayRecord = new MchAgentpayRecord();
        mchAgentpayRecord.setMchId(mchId);
        mchAgentpayRecord.setMchOrderNo(mchOrderNo);
        return find(mchAgentpayRecord);
    }

    @Override
    public List<MchAgentpayRecord> select(int offset, int limit, List<Byte> statusList, MchAgentpayRecord mchAgentpayRecord) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        example.setOrderByClause("createTime desc");
        example.setOffset(offset);
        example.setLimit(limit);
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord);
        criteria.andStatusIn(statusList);
        return mchAgentpayRecordMapper.selectByExample(example);
    }

    @Override
    public int count(List<Byte> statusList, MchAgentpayRecord mchAgentpayRecord) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        setCriteria(criteria, mchAgentpayRecord);
        criteria.andStatusIn(statusList);
        return mchAgentpayRecordMapper.countByExample(example);
    }

    /** 更新响应数据 **/
    @Override
    public int updateMchConfirmResp(String agentpayOrderId, String resp){

        if(resp == null){
            resp = "";
        }

        if(resp.length() > 512){
            resp = resp.substring(0, 512); //避免数量太长导致数据库报错
        }

        MchAgentpayRecord updateRecord = new MchAgentpayRecord();
        updateRecord.setAgentpayOrderId(agentpayOrderId);
        updateRecord.setMchConfirmResp(resp);
        return mchAgentpayRecordMapper.updateByPrimaryKeySelective(updateRecord);
    }

    /** 商户二次确认成功后的处理 **/
    @Override
    public int updateStatus4WaitMchComfirmSuccess(String agentpayOrderId, Byte updateRecordStatus, TransOrder transOrder){

        MchAgentpayRecord updateRecord = new MchAgentpayRecord(); updateRecord.setStatus(updateRecordStatus);

        if(transOrder != null){
            updateRecord.setTransOrderId(transOrder.getTransOrderId());
        }

        MchAgentpayRecordExample exa = new MchAgentpayRecordExample();
        exa.createCriteria().andAgentpayOrderIdEqualTo(agentpayOrderId).andStatusEqualTo(PayConstant.AGENTPAY_STATUS_WAIT_API_CONFIRM);
        int updateRow = mchAgentpayRecordMapper.updateByExampleSelective(updateRecord, exa);
        if(updateRow <= 0){
            return updateRow;
        }

        if(transOrder != null){
            updateRow += transOrderService.createTransOrder(transOrder);
        }

        return updateRow;
    }

    @Override
    @DataSourceSwitch(DataSourceType.SLAVE)
    public Map count4All(Long mchId, Long supplierExecId, String accountName, String agentpayOrderId, String transOrderId, Byte status, Byte agentpayChannel, String mchOrderNo, String operatorUserId, String createTimeStart, String createTimeEnd) {
        Map param = new HashMap<>();
        if(mchId != null) param.put("mchId", mchId);
        if(supplierExecId != null) param.put("supplierExecId", supplierExecId);
        if(StringUtils.isNotBlank(agentpayOrderId)) param.put("agentpayOrderId", agentpayOrderId);
        if(StringUtils.isNotBlank(transOrderId)) param.put("transOrderId", transOrderId);
        if(StringUtils.isNotBlank(accountName)) param.put("accountName", accountName);
        if(status != null && status != -99) param.put("status", status);
        if(agentpayChannel != null && agentpayChannel != -99) param.put("agentpayChannel", agentpayChannel);
        if(StringUtils.isNotBlank(mchOrderNo)) param.put("mchOrderNo", mchOrderNo);
        if(StringUtils.isNotBlank(operatorUserId)) param.put("operatorUserId", operatorUserId);
        if(StringUtils.isNotBlank(createTimeStart)) param.put("createTimeStart", createTimeStart);
        if(StringUtils.isNotBlank(createTimeEnd)) param.put("createTimeEnd", createTimeEnd);
        return mchAgentpayRecordMapper.count4All(param);
    }

    @Transactional
    @Override
    public int updateByReissue(String agentpayOrderId, Byte updateRecordStatus){

        MchAgentpayRecord record = mchAgentpayRecordMapper.selectByPrimaryKey(agentpayOrderId);
        String transOrderId = record.getTransOrderId();

        Byte conditionRecordStatus = null; //条件状态
        Byte updateTransResultStatus = null; //转账订单更新状态

        if(updateRecordStatus == PayConstant.AGENTPAY_STATUS_SUCCESS){ //失败 --》 成功

            conditionRecordStatus = PayConstant.AGENTPAY_STATUS_FAIL;
            updateTransResultStatus = PayConstant.TRANS_RESULT_SUCCESS;
        }else if(updateRecordStatus == PayConstant.AGENTPAY_STATUS_FAIL){  //成功 --》 失败

            conditionRecordStatus = PayConstant.AGENTPAY_STATUS_SUCCESS;
            updateTransResultStatus = PayConstant.TRANS_RESULT_FAIL;
        }else{

            return -1;
        }

        MchAgentpayRecord updateRecord = new MchAgentpayRecord(); updateRecord.setStatus(updateRecordStatus);
        MchAgentpayRecordExample exa = new MchAgentpayRecordExample();
        exa.createCriteria().andAgentpayOrderIdEqualTo(agentpayOrderId).andStatusEqualTo(conditionRecordStatus);
        int updateRow = mchAgentpayRecordMapper.updateByExampleSelective(updateRecord, exa);
        if(updateRow <= 0){
            return updateRow;
        }

        if(StringUtils.isNotEmpty(transOrderId)){
            TransOrder updateRecordByTrans = new TransOrder();
            updateRecordByTrans.setTransOrderId(transOrderId); updateRecordByTrans.setResult(updateTransResultStatus);
            updateRecordByTrans.setStatus(PayConstant.TRANS_STATUS_COMPLETE);
            updateRow += transOrderMapper.updateByPrimaryKeySelective(updateRecordByTrans);
        }

        return updateRow;
    }

    @Override
    public List<MchAgentpayRecord> selectList(List<String> idList) {
        MchAgentpayRecordExample example = new MchAgentpayRecordExample();
        example.setOrderByClause("createTime DESC");
        MchAgentpayRecordExample.Criteria criteria = example.createCriteria();
        criteria.andAgentpayOrderIdIn(idList);
        return mchAgentpayRecordMapper.selectByExample(example);
    }

    void setCriteria(MchAgentpayRecordExample.Criteria criteria, MchAgentpayRecord mchAgentpayRecord) {
        setCriteria(criteria, mchAgentpayRecord, null);
    }

    void setCriteria(MchAgentpayRecordExample.Criteria criteria, MchAgentpayRecord mchAgentpayRecord, JSONObject queryObj) {
        if(mchAgentpayRecord != null) {
            if(mchAgentpayRecord.getMchId() != null) criteria.andMchIdEqualTo(mchAgentpayRecord.getMchId());
            if(StringUtils.isNotBlank(mchAgentpayRecord.getTransOrderId())) criteria.andTransOrderIdEqualTo(mchAgentpayRecord.getTransOrderId());
            if(StringUtils.isNotBlank(mchAgentpayRecord.getAgentpayOrderId())) criteria.andAgentpayOrderIdEqualTo(mchAgentpayRecord.getAgentpayOrderId());
            if(StringUtils.isNotBlank(mchAgentpayRecord.getMchOrderNo())) criteria.andMchOrderNoEqualTo(mchAgentpayRecord.getMchOrderNo());
            if(StringUtils.isNotBlank(mchAgentpayRecord.getAccountName())) criteria.andAccountNameEqualTo(mchAgentpayRecord.getAccountName());
            if(mchAgentpayRecord.getStatus() != null && mchAgentpayRecord.getStatus().byteValue() != -99) criteria.andStatusEqualTo(mchAgentpayRecord.getStatus());
            if(mchAgentpayRecord.getAgentpayChannel() != null && mchAgentpayRecord.getAgentpayChannel().byteValue() != 99) criteria.andAgentpayChannelEqualTo(mchAgentpayRecord.getAgentpayChannel());
            if(StringUtils.isNotBlank(mchAgentpayRecord.getOperatorUserId())) criteria.andOperatorUserIdEqualTo(mchAgentpayRecord.getOperatorUserId());
        }
        if(queryObj != null) {
            if(queryObj.getDate("createTimeStart") != null) criteria.andCreateTimeGreaterThanOrEqualTo(queryObj.getDate("createTimeStart"));
            if(queryObj.getDate("createTimeEnd") != null) criteria.andCreateTimeLessThanOrEqualTo(queryObj.getDate("createTimeEnd"));
        }
        if(mchAgentpayRecord.getPs() != null){
            //创建时间 >= xx (字符串类型， 标准日期时间格式)
            if(StringUtils.isNotEmpty(mchAgentpayRecord.getPsStringVal("createdAtGeStr"))){
                criteria.andCreateTimeGreaterThanOrEqualTo(DateUtil.str2date(mchAgentpayRecord.getPsStringVal("createdAtGeStr")));
            }
            if(StringUtils.isNotEmpty(mchAgentpayRecord.getPsStringVal("updateAtGeStr"))){
                criteria.andUpdateTimeGreaterThanOrEqualTo(DateUtil.str2date(mchAgentpayRecord.getPsStringVal("updateAtGeStr")));
            }
        }

    }

}
